// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: SAM_MemoryInterface.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2005, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include "Ni/Ni_Strand.h"
#include "Ni/Ni_Core.h"
#include "Ni/Ni_Cpu.h"
#include "SAM_MemoryInterface.h"
#include "StdMacros.h"
#include "Util/NumericUtils.h"
#include "Blaze_CBInterface.h"
#include <iomanip>
#include <sstream>
#include <cassert>
#include <fstream>
#include <unistd.h>

namespace Sam {
#include "mem.h"   // sam mem interface
}

using namespace std;
using namespace Riesling;
using namespace Sam; 



#define MEMOP_LOAD 0
#define MEMOP_INSTR 0x4455
#define MEMOP_NO_LOAD 0x2233

#define NI_PA_BIT   (0x8000000000LLU)
#define JBUS_PA_BIT (0x40000000000LLU)
#define INT_VEC_DIS_ADDR (0x9800000800LLU)

#define is_ram_addr(PA) (((PA & NI_PA_BIT) == 0) && ((PA & JBUS_PA_BIT)==0)) //|| (PA > 0xfff0000000) rom address

#define is_dumbserial_addr(pa) \
  (((pa) >= 0x1f00000000ULL && (pa) < 0x1f00000050ULL) || \
   ((pa) >= 0x1f10002000ULL && (pa) < 0x1f10002050ULL) || \
   ((pa) >= 0x1f10000000ULL && (pa) < 0x1f10000050ULL))


#define is_jbus_addr(pa) \
  ((((pa) & 0xFF00000000ULL) == 0x8000000000ULL) || (((pa) & 0xFF00000000ULL) == 0xf000000000ULL))
#define jbus_nccr(pa) \
  (((pa) & 0xFFFFFFFFULL) | JBUS_PA_BIT)
  
#define CHECK_JBUS_ADDR(PA) if(is_jbus_addr(PA)) PA=jbus_nccr(PA);


inline uint64_t mem_read ( SMemory *pMem, uint64_t pa, int size )
{
    uint64_t v = 0; 

    switch (size) 
    {
        case 1:  v = memread8u  (pMem, pa); break;
        case 2:  v = memread16u (pMem, pa); break; 
        case 4:  v = memread32u (pMem, pa); break; 
        case 8:  v = memread64u (pMem, pa); break; 
        default: break; 
    }
    return v; 

}
 

inline void mem_write ( SMemory *pMem, uint64_t pa, uint64_t data, int size )
{
    switch (size) 
    {
        case 1:  memwrite8u  (pMem, pa, data); break;
        case 2:  memwrite16u (pMem, pa, data); break; 
        case 4:  memwrite32u (pMem, pa, data); break; 
        case 8:  memwrite64u (pMem, pa, data); break; 
        default: break; 
    }
    return; 
}

 





// constructor
Sam_MemoryInterface::Sam_MemoryInterface( Sam::VCPU_ImpIntf* interface ) 
{
    this->sys_interface = *interface; 
}


// copy constructor
Sam_MemoryInterface::Sam_MemoryInterface( const Sam_MemoryInterface & orig )    
{  
    this->sys_interface = orig.sys_interface;
}


// destructor
Sam_MemoryInterface::~Sam_MemoryInterface()
{
}

// assignment operator
const Sam_MemoryInterface &
Sam_MemoryInterface::operator=( const Sam_MemoryInterface & rhs )
{
    this->sys_interface = rhs.sys_interface;
    return *this;
}



//equality operator
bool
Sam_MemoryInterface::operator==( const Sam_MemoryInterface & rhs ) const
{ 
    if( 
         (this->sys_interface.access_io == rhs.sys_interface.access_io) &&
         (this->sys_interface.access_serial == rhs.sys_interface.access_serial) &&
         (this->sys_interface.vtrace == rhs.sys_interface.vtrace)
      )
        return true; 
    else
        return false; 
}




std::string Sam_MemoryInterface::toString() const
{
    ostringstream os;
    assert (0); 
    
    return os.str();
}
       
   



void
Sam_MemoryInterface::read( MemoryTransaction& memXact )
{
    uint32_t strand_id = memXact.getStrand()->getId();
    uint64_t pa = memXact.paddr();
    uint64_t v = 0;


    
    if (memXact.referenceType() ==  MemoryTransaction::INSTR) 
    {
        // instruction fetch
        
        if (memXact.size() == 4) 
        {
           //if (is_ram_addr(pa))
               v = mem_read (sys_interface.mem, pa, 4);
           //else
           //    sys_interface.access_io (strand_id, 0, pa, 4, v, ~0); 

           memXact.setData(v); 
        }
        else 
        {
           uint64_t buf[8];
           
           for (int i=0; i<8; i++) 
           {
               // if (is_ram_addr(pa))
                   buf[i] = mem_read (sys_interface.mem, pa+i*8, 8);  
               //else 
               //   sys_interface.access_io (strand_id, 0, pa+i*8, 8, buf[i], ~0); 
           }
           memXact.setData(buf);
        }      
        return;
    }
  
    // data load
    int size = memXact.size();

    // convert console access 
    if ((size) == 1 && is_dumbserial_addr(pa)) 
    {
             pa |= NI_PA_BIT;
    }

    (*BlazeCBInterface::getblazereadcb())(strand_id, MEMOP_LOAD, memXact.vaddr(), memXact.paddr(), memXact.size());

    if (size <= 8) 
    {
        if ( is_ram_addr(pa) )
        {
             v = mem_read (sys_interface.mem, pa, size);
        }
        else
        {
             if (size==8) CHECK_JBUS_ADDR(pa);
             sys_interface.access_io (strand_id, 0, pa, size, v, ~0);
        }
        memXact.setData(v); 
    }
    else 
    {
        if (size & 0x7) 
        {
            RS_DOMAIN_ERR("Read of size %d, is not a multiple of 8 bytes", size);
        }
    
        size = size / 8;
        for (uint_t i = 0; i < size; ++i) 
        {
            if (is_ram_addr(pa))
            {
                 v = mem_read (sys_interface.mem, pa+ i * 8, 8);
            }
            else
            {
                 sys_interface.access_io (strand_id, 0, pa+ i * 8, 8, v, ~0);
            }

            memXact.setData(i, v);

        }
    }
}

void Sam_MemoryInterface::lock (uint64_t pa, RsLockOpT lockOp)
{
  //(*BlazeCBInterface::getblazelockcb())(pa, lockOp);
}


void
Sam_MemoryInterface::write( MemoryTransaction& memXact )
{
    uint32_t strand_id = memXact.getStrand()->getId();
  
    
    uint64_t pa = memXact.paddr();

    (*BlazeCBInterface::getblazewritecb())(strand_id, memXact.vaddr(), memXact.paddr(), memXact.getData(), memXact.size(), memXact.get_byte_mask());

    if (memXact.size() <= 8) 
    {
         uint64_t data = memXact.getData(); 

         // convert console access 
         if ( (memXact.size() == 1) &&  is_dumbserial_addr(pa) ) 
         {
             pa |= NI_PA_BIT;
         }

         if (is_ram_addr(pa))
         {
             mem_write ( sys_interface.mem, pa, data, memXact.size() ); 
         }
         else
         {
             if (memXact.size()==8) {
	       if (pa == INT_VEC_DIS_ADDR) {
		 int vc_id = (data >> 8) & 0x1f;
		 int s = vc_id & 0x3;
		 int c = (vc_id >> 2) & 0x7;
		 Ni_Strand *sp = (Ni_Strand *)memXact.getStrand();
		 Ni_Strand *dp = (Ni_Strand *)sp->getCore().getCpu().getCorePtr(c)->getStrandPtr(s);
		 int type = (data >> 16) & 0x3;
		 switch (type) {
		 case 2:
		   // Should sendIdleMessage but it ignores me :-(
		   dp->sendHaltedMessage();
		   break;
		 case 3:
		   dp->sendResumeMessage();
		   break;
		 default:
		   fprintf(stderr, "FIXME: write 0x%llx to addr 0x%llx\n", data, pa);
		   break;
		 }
		 return;
	       }
	       else
		 CHECK_JBUS_ADDR(pa);
	     }
             sys_interface.access_io (strand_id, 1, pa, memXact.size(), 
                                      data, memXact.get_byte_mask()); 
         }
           
    }
    else 
    {
      if (memXact.size() % 8) 
      {
           RS_DOMAIN_ERR("Write of size %d, is not a multiple of 8 bytes",
                          memXact.size());
      }
      uint_t size = memXact.size() / 8;
      for (uint_t i = 0; i < size; ++i) 
      {
         uint64_t data = memXact.getData(i);

         if (is_ram_addr(pa))
         {
             mem_write ( sys_interface.mem, pa+i*8, data, 8 );
         }
         else
         {
             sys_interface.access_io (strand_id, 1, pa+i*8, 8, data, ~0); 

         }

      }
    }
}


uint64_t
Sam_MemoryInterface::read( PaddrT address, uint8_t size )
{
    uint64_t data = 0;


    PRINT_DEBUG( "Read of %d bytes of data from 0x%0.16llx data=%0.16llx\n", size, address, data );

    assert (0); //  method  is not used
    return data;
}


void
Sam_MemoryInterface::write( PaddrT address, uint64_t data, uint8_t size )
{
     RS_INVALID_ARG( "Write of size %d is not supported. Writes must be 1, 2, 4, or 8 bytes.", size );
     assert(0);
}


